
/*
 * (a)  (C) 1990 by Adobe Systems Incorporated. All rights reserved.
 *
 * (b)  If this Sample Code is distributed as part of the Display PostScript
 *	System Software Development Kit from Adobe Systems Incorporated,
 *	then this copy is designated as Development Software and its use is
 *	subject to the terms of the License Agreement attached to such Kit.
 *
 * (c)  If this Sample Code is distributed independently, then the following
 *	terms apply:
 *
 * (d)  This file may be freely copied and redistributed as long as:
 *	1) Parts (a), (d), (e) and (f) continue to be included in the file,
 *	2) If the file has been modified in any way, a notice of such
 *      modification is conspicuously indicated.
 *
 * (e)  PostScript, Display PostScript, and Adobe are registered trademarks of
 *	Adobe Systems Incorporated.
 * 
 * (f) THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO
 *	CHANGE WITHOUT NOTICE, AND SHOULD NOT BE CONSTRUED
 *	AS A COMMITMENT BY ADOBE SYSTEMS INCORPORATED.
 *	ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY
 *	OR LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO
 *	WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR STATUTORY)
 *	WITH RESPECT TO THIS INFORMATION, AND EXPRESSLY
 *	DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, 
 *	FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT
 *	OF THIRD PARTY RIGHTS.
 */

/*
 *	EpsfParser.m
 *
 *	This class reads in a distilled epsf file and attempts to parse through the file
 *	and insert the paths into the programs data structures.
 *
 *	Version:	2.0
 *	Author:	Ken Fromm
 *	History:
 *			03-07-91		Added this comment.
 */

#import "EpsfParser.h"
#import "ScrollApp.h"
#import "iparser.h"

#import <appkit/Panel.h>
#import <appkit/nextstd.h>
#import <stdio.h>

static char  *processToLineEnd();
static char  *processToField();

static BOOL checkForPS(char **dataptr, float *level_dsc, float *level_epsf)
{
	char 	*dp;
	
	BOOL	invalid_ps = YES;

	*level_dsc = *level_epsf = 0.0;

	dp = *dataptr;
	if (!strncmp(dp, "%!PS-Adobe-", 11))
	{
		invalid_ps = NO;

		dp +=11;
		sscanf(dp, "%f", level_dsc);

		while(*dp == ' ') dp++;
		if (!strncmp(dp, "EPSF-", 5))
		{
			dp += 5;
			sscanf(dp, "%f", level_dsc);
		}
	}
	
	*dataptr = processToLineEnd(dp);

	return invalid_ps;
}

static int checkType(char *s0, char *s1, char *s2, char *s3)
{
	int	type = PS_NOTYPE;
	
	if (s0 && *s0 && s1 && *s1)
	{
		if (!strncmp(s0, "Adobe", 5) && !strncmp(s1, "Illustrator", 11))
			type = PS_ILLUSTRATOR;
		else if (!strncmp(s0, "Glenn", 5) &&
			    !strncmp(s1, "Reid", 4) &&
			    !strncmp(s3, "still.ps", 8))
			type = PS_DISTILLERY;
	}

	return type;
}

static BOOL validateCreator(int ps_creator)
{
	if (ps_creator == PS_DISTILLERY)
		return YES;
	else
		return NO;
}

static void convertBounds(float *aBox, NXRect *aRect)
{
	aRect->origin.x = aBox[0];
	aRect->origin.y = aBox[1];
	aRect->size.width = aBox[2] - aBox[0];
	aRect->size.height = aBox[3] - aBox[1];
}
			
static char *processToLineEnd(char *dp)
{
	dp = strchr(dp,'\n'); dp++;
	
	return dp;
}

static char *processToField(char *dp)
{

	dp = strchr(dp,':'); dp++;
	if (*dp == ' ')
	{
		strrchr(dp, ' ');
		dp++;
	}
	
	return dp;
}

static void  checkHeader(char **dataptr, float *bbox, BOOL *validBBox, int  *creator)
{
	BOOL	done = NO;

	char		*dp;
	
	char		str0[40], str1[40], str2[40], str3[40];

	int		matches = 0;

	*creator = PS_NOTYPE;
	*validBBox = NO;

	dp = *dataptr;
	while (*dp && !done)
	{
		if (strncmp(dp, "%%", 2) == 0)
		{
			dp += 2;
			if (strncmp(dp, "EndComments", 11) == 0)
				done = YES;
			else if (strncmp(dp, "Creator:", 8) == 0)
			{
				dp = processToField(dp);
				sscanf(dp, "%s%s%s%s", str0, str1, str2, str3);
				*creator = checkType(str0, str1, str2, str3);
			}
			else if (strncmp(dp, "BoundingBox:", 12) == 0)
			{
				dp = processToField(dp);
				if (strncmp(dp, "(atend)", 7) != 0)
				{
					matches = sscanf(dp, "%f %f %f %f",
						&bbox[0], &bbox[1], &bbox[2], &bbox[3]);
					if ((matches && matches != 4) ||
			 		    (matches == 4 &&
					    (bbox[2] == bbox[0] || bbox[3] == bbox[1])))
						*validBBox = YES;
				}
			}
		}	
		dp = processToLineEnd(dp);
	}

	*dataptr = dp;
	return;
}

static void  checkTrailer(char **dataptr, float *bbox, BOOL *validBBox)
{
	BOOL	done = NO;

	char		*dp;
	
	int		matches = 0;

	*validBBox = NO;

	dp = *dataptr;
	while (*dp && !done)
	{
		if (strncmp(dp, "%%", 2) == 0)
		{
			dp += 2;
			if (strncmp(dp, "BoundingBox:", 12) == 0)
			{
				dp = processToField(dp);
				matches = sscanf(dp, "%f %f %f %f",
						&bbox[0], &bbox[1], &bbox[2], &bbox[3]);
				if (matches == 4 && bbox[2] != bbox[0] && bbox[3] != bbox[1])
				 {
					 done = YES;
					*validBBox = YES;
				}
			}
		}
		dp = processToLineEnd(dp);
	}

	*dataptr = dp;
	return;
}

@implementation EpsfParser

/*
 * Checks for valid PS file, gets the bounding box and checks for
 * the creator. The creator will let us determine whether we can read
 * the file into our own graphics structure.
 */
- readPSFromStream:(NXStream *)stream
{
	id		graphic;

	BOOL	error = YES, validBBox = NO;

	char 	*data;
	
	float		bbox[4];

	int 		length,		/* the number of bytes in data */
			maxlen;		/* size of vm_allocated region */

	bbox[0] = bbox[1] = bbox[2] = bbox[3] = 0;

	NXGetMemoryBuffer(stream, &data, &length, &maxlen);
	if (length)
	{
		error = checkForPS(&data, &level_dsc, &level_epsf);
		if (!error)
		{
			error = YES;
			checkHeader(&data, bbox, &validBBox, &creator);
			if (validateCreator(creator))
			{
				if (!iparse(&data))
				{
					if (!validBBox)
						checkTrailer(&data, bbox, &validBBox);
					
					if (validBBox)
					{
						convertBounds(bbox, &bounds);
						error = NO;
					}
					else
						NXRunAlertPanel("Load File",
						"Invalid bounding box. Cannot open.", 
						"OK", NULL, NULL);
				}
			}
			else
				NXRunAlertPanel("Load File",
					"Not a distilled file. Cannot open.", 
					"OK", NULL, NULL);
		}
		else
			NXRunAlertPanel("Load File", "Not a valid PS file. Cannot open.", 
					"OK", NULL, NULL);	
	}
	else
		NXRunAlertPanel("Load File", "Invalid file. Cannot open.", 
				"OK", NULL, NULL);	

	if (error)
		return nil;
	else
		return self;
}

- getBounds:(NXRect *) aRect
{
	*aRect = bounds;
}

@end

